# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals


# useful for handling different item types with a single interface


class FirstscrapySpiderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the spider middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        """
        如果存在，则调用该类方法从 Crawler . 它必须返回中间件的新实例。爬虫对象提供对所有零碎核心组件（如设置和信号）的访问；它是中间件访问它们并将其功能连接到零碎的一种方式。
        :param crawler: 使用此中间件的爬虫程序
        :return:
        """
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        """
        对于通过蜘蛛中间件进入蜘蛛进行处理的每个响应，都会调用此方法。
        如果它回来 None ，scrapy将继续处理此响应，执行所有其他中间软件，
        直到最后将响应提交给spider进行处理
        :param response: 正在处理的响应
        :param spider: 此响应所针对的蜘蛛
        :return:
        """
        return None

    def process_spider_output(self, response, result, spider):
        """
        在处理完响应后，调用此方法，并返回蜘蛛返回的结果。
        当spider处理response后返回result时，这个方法被调用，
        必须返回Request或Item对象的可迭代对象，一般返回result
        :param response: 从spider生成此输出的响应
        :param result: 蜘蛛返回的结果
        :param spider: 正在处理结果的蜘蛛
        :return:
        """
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):
        """
        当蜘蛛或 process_spider_output() 方法（来自以前的蜘蛛中间件）引发异常。
        process_spider_exception() 应该返回 None 或可迭代的 Request 对象和 item object 。

        如果它回来 None ，Scrapy将继续处理此异常，执行任何其他 process_spider_exception() 在下面的中间件组件中，直到没有中间件组件离开并且异常到达引擎（在那里记录并丢弃它）。
        如果它返回一个不可数 process_spider_output() 管道启动，从下一个蜘蛛中间件开始，没有其他 process_spider_exception() 将被调用。

        :param response: 引发异常时正在处理的响应
        :param exception: 引发的异常
        :param spider: 引发异常的蜘蛛
        :return:
        """
        pass

    def process_start_requests(self, start_requests, spider):
        """
        这个方法是用spider的start请求调用的，其工作原理与 process_spider_output() 方法，但它没有关联的响应，必须只返回请求（而不是项）。
        它接收一个可迭代的(在 start_requests 参数)，并且必须返回另一个可迭代的 Request 对象。

        在您的蜘蛛中间件中实现此方法时，您应该始终返回一个ITerable（跟随输入的ITerable），而不是全部消费。 start_requests
        迭代器，因为它可能非常大（甚至没有边界），并导致内存溢出。scrappy引擎设计用于在有能力处理启动请求的情况下提取它们，因此，如果存在停止spider
        的其他条件（如时间限制或项目/页面计数），则启动请求迭代器实际上可以是无止境的。
        :param start_requests: 启动请求
        :param spider: 启动请求所属的蜘蛛
        :return:
        """
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)


class FirstscrapyDownloaderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        return None

    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response

    def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.

        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)
